//#include "stdafx.h"
//#include "utility_stream.h"
#include "stream/ostream_out.h"
#include "utility/console.h"
#include "thread/Thread.h"
#include "utility/Mutex.h"
#include "utility/SafeQueue.h"
#include "file/file.h"
#include "utility/os.h"

#include "utility/hashmap.h"
#include "win.h"

#include <sstream>
#include <string>
#include<stdio.h>

//#include "utility_new.h"


#pragma warning(disable: 4996)
using namespace std;

/////////////////////////////////////////////////////////////////////////////
namespace KCore
{
	ostream_cout::ostream_cout() : std::stringstream()
	{
		m_type = cout_out;
		m_color = white;
	}

	//stream_end g_endl;
	bool ostream_cout::m_thread = false;
	color_enum g_color = white;

	typedef std::pair<color_enum,std::string> color_string;
	typedef SafeQueue<color_string> safe_list_cs;

	class cout_buffer
	{
	public:
		safe_list_cs m_list_print;
		safe_list_cs m_list_file;
		bool m_print,m_file;
		string m_filename;
		color_enum m_color;
	public:
		void set_out(int color,bool print,bool file,const char* filename)
		{
			m_color = (color_enum)color;
			if (m_color < 0 || m_color >= 16)
				m_color = white;
			m_print = print;
			m_file = file;
			m_filename = filename;
			if (m_file)
			{
				string s = help_getcurtimestr() + " start\n";
				append_filestring_txt(filename,s);
			}
		}
		cout_buffer(){
			m_print = true;
			m_file = false;
			m_color = white;
		}
		virtual ~cout_buffer(){
			flush_print();
			flush_file();
			if (m_file)
			{	
				string s = help_getcurtimestr() + " end\n\n";
				append_filestring_txt(m_filename.c_str(),s);
			}
		}
		void push_back(const char *buf,int len,int color = -1)
		{
			if (color == -1 && m_color != -1)
				color = m_color;
			return push_back(string(buf,len),color);
		}
		void push_back_file(const std::string& str,int color = -1)
		{
			if (m_file)
				m_list_file.push_back(std::make_pair((color_enum)color,str));
		}
		void push_back(const std::string& str,int color = -1)
		{
			if (m_print)
				m_list_print.push_back(std::make_pair((color_enum)color,str));
			push_back_file(str,color);
		}
		void flush_file()
		{
			if (m_file)
			{
				safe_list_cs::list_t& m_list = m_list_file.pop();
				if (!m_list.empty())
				{
					string s;
					for (safe_list_cs::list_t::iterator it = m_list.begin(); it != m_list.end(); ++it)
					{
						s += it->second;
					}
					append_filestring_txt(m_filename.c_str(),s);
				}
			}
		}
		void flush_print()
		{
			safe_list_cs::list_t& m_list = m_list_print.pop();
			if (!m_list.empty())
			{
				for (safe_list_cs::list_t::iterator it = m_list.begin(); it != m_list.end(); ++it)
				{
					color_enum color = (*it).first;
					if (color != g_color)
					{
						g_color = color;
						change_fg_color((KCore::color_enum)g_color);
					}
					//std::cout << m_list.front().second.c_str() << std::endl;
					printf((*it).second.c_str());
				}
			}
		}
	};

	typedef std::pair<std::string,std::string> file_string;
	typedef SafeQueue<file_string> safe_list_fs;

	class file_buffer
	{
	public:
		safe_list_fs m_list;
		void push_back(const char* filename,const std::string& str){
			m_list.push_back(std::make_pair(filename,str));
		}
		void flush()
		{
			safe_list_fs::list_t& fs = m_list.pop();
			hash_map<std::string,std::string > hm;
			for (safe_list_fs::list_t::iterator it = fs.begin(); it != fs.end(); ++it)
			{
				file_string& fs2 = *it;
				hm[fs2.first] += fs2.second;
			}
			for (hash_map<std::string,std::string >::iterator it = hm.begin();
				it != hm.end(); ++it)
				append_filestring_txt(it->first.c_str(),it->second);
		}
		~file_buffer()
		{
			flush();
		}
	};

	struct st_file_binary
	{
		std::string file;
		std::string str;
		long flag;	
		st_file_binary(const std::string& f,const std::string& s,long fl):file(f),str(s),flag(fl){}
	};
	typedef SafeQueue<st_file_binary> safe_list_fb;

	string to_string_binary(const std::string& s,long f)
	{
		std::ostringstream os;
		os << "index " << f << " ";
		hexdump(os,s.data(),s.size());
		return os.str();
	}

	class file_buffer_binary
	{
	public:
		safe_list_fb m_list;
		void push_back(const char* filename,const std::string& str,long flag){
			m_list.push_back(st_file_binary(filename,str,flag));
		}
		void flush()
		{
			safe_list_fb::list_t& fs = m_list.pop();
			hash_map<std::string,std::string > hm;
			for (safe_list_fb::list_t::iterator it = fs.begin(); it != fs.end(); ++it)
			{
				st_file_binary& fs2 = *it;
				hm[fs2.file] += to_string_binary(fs2.str,fs2.flag);
			}
			for (hash_map<std::string,std::string >::iterator it = hm.begin();
				it != hm.end(); ++it)
	
				append_filestring_txt(it->first.c_str(),it->second);
		}
		~file_buffer_binary()
		{
			flush();
		}
	};

	cout_buffer m_buffer_cout[cout_type_num];
	file_buffer m_buffer_file;
	file_buffer_binary m_buffer_file_binary;

	Thread g_thread;

	void thread_add_buffer(ostream_cout_enum type,color_enum color,const char *buf,int len)
	{
		m_buffer_cout[type].push_back(buf,len,color);
	}

	void ostream_cout::flush_string(ostream_cout_enum type,const std::stringstream& os,int color)
	{
		return flush_string(type,os.str(),color);
	}
	void ostream_cout::flush_string(ostream_cout_enum type,const std::string& str,int color)
	{
		if (color < 0 || color >= 16)
			color = m_buffer_cout[type].m_color;
		if (!m_thread)
		{
			if(m_buffer_cout[type].m_print)
			{
				if (color != KCore::g_color)
				{
					KCore::g_color = (color_enum)color;
					change_fg_color((KCore::color_enum)color);
				}
				//std::cout << str.c_str() << std::endl;
				printf(str.c_str());
			}
			if (m_buffer_cout[type].m_file)
				append_filestring_txt(m_buffer_cout[type].m_filename.c_str(),str);
		}
		else
		{
			m_buffer_cout[type].push_back(str,color);
		}
	}

	void ostream_cout::flush_cout()
	{
		if (m_color != g_color)
		{
			g_color = m_color;
			change_fg_color((KCore::color_enum)m_color);
		}
		//*m_pointer = 0;
		//std::cout << m_buffer << std::endl;
		//printf(m_buffer);
		//m_pointer = m_buffer;
		printf(str().c_str());
		str("");
	}
	void ostream_cout::flush_thread()
	{
		//*m_pointer = 0;
		//thread_add_buffer(m_type,m_color,m_buffer,str().length());
		//m_pointer = m_buffer;
		std::string temp = str();
		thread_add_buffer(m_type,m_color,temp.c_str(),temp.length());
		str("");
	}

	void flush_out()
	{
		for (int i=0; i<cout_type_num; i++)
		{
			cout_buffer& buffer = m_buffer_cout[i];
			buffer.flush_print();
		}
	}
	void flush_out_file()
	{
		for (int i=0; i<cout_type_num; i++)
		{
			cout_buffer& buffer = m_buffer_cout[i];
			buffer.flush_file();
		}
	}
	void flush_files()
	{
		m_buffer_file.flush();
		m_buffer_file_binary.flush();
	}

	void log_flush_file(){
		flush_out_file();
		flush_files();
	}

    #ifdef __LINUX
	DWORD  cout_run_thread(void* pviod )
	#else
	DWORD WINAPI cout_run_thread(LPVOID )
	#endif
	{
		int cout_num = 0;
		while(g_thread.waitKillEvent(100))
		{
			flush_out();
			cout_num++;
			if (cout_num == 1000)
			{
				cout_num = 0;
				log_flush_file();
			}
		}
		return 0;
	}

	void cout_start_thread()
	{
		ostream_cout::m_thread = true;
		g_thread.runThread(cout_run_thread,0);
	}

	void cout_end_thread()
	{
		ostream_cout::m_thread = false;
		g_thread.endThread();
	}

	void cout_set_type(ostream_cout_enum type,int color,bool m_print,bool file,const char* filename)
	{
		cout_buffer& buffer = m_buffer_cout[type];
		buffer.set_out(color,m_print,file,filename);
	}

	void cout_set_type_by_exename(ostream_cout_enum type,int color,bool m_print,bool file,const char* filename, const char* path_work)
	{
		string f = path_work;
		if (!f.empty())
		{
			f = f + (string)"/";
		}
		f = f + (string)"log/" + os_getexename() + "/" + filename;
		dir::md_all_forfile(f.c_str());
		cout_set_type(type,color,m_print,file,f.c_str());
	}

	bool overwrite_file_stream(const char* filename,const std::stringstream& os)
	{
		dir::md_all_forfile(filename);
		FILE* f = fopen(filename,"wb");
		if (!f)
			return false;
		std::string temp = os.str();
		fwrite(temp.c_str(),1,temp.length(),f);
		fclose(f);
		return true;
	}
	bool append_file_stream(const char* filename,const std::stringstream& os)
	{
		dir::md_all_forfile(filename);
		FILE* f = fopen(filename,"ab");
		if (!f)
			return false;
		std::string temp = os.str();
		fwrite(temp.c_str(),1,temp.length(),f);
		fclose(f);
		return true;
	}
	void stream_overwrite_file(const char* filename,const std::string& str)
	{
		dir::md_all_forfile(filename);
		FILE* f = fopen(filename,"wb");
		if (f)
			fclose(f);
		stream_append_file(filename,str);
	}
	void stream_append_file(const char* filename,const std::string& str)
	{
		if (ostream_cout::m_thread)
			m_buffer_file.push_back(filename,str);
		else
			append_filestring_txt(filename,str);
	}
	void stream_append_file_binary(const char* filename,const char *buffer,size_t len,int flag)
	{
		if (ostream_cout::m_thread)
			m_buffer_file_binary.push_back(filename,string(buffer,len),flag);
		else
			append_filestring_txt(filename,to_string_binary(string(buffer,len),flag));
	}

	
}

